package com.kxf.checkapksoplugin

import com.kxf.checkapksoplugin.bean.SoFile
import com.kxf.checkapksoplugin.bean.SoFileDiffList
import com.kxf.checkapksoplugin.util.CommandUtils
import com.kxf.checkapksoplugin.util.Utils
import org.codehaus.groovy.runtime.StringGroovyMethods.isBlank
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import java.io.BufferedReader
import java.io.File
import java.io.InputStreamReader


class CheckApkSoPlugin : Plugin<Project> {

    companion object {
        var logFlag = false
    }

    val GROUP_NAME = "kxf"
    val TASK_NAME = "CheckApkSoPlugin"

    var ABI_TYPES = arrayOf("armeabi-v7a", "armeabi", "arm64-v8a", "x86", "mips64", "mips")

    var fileSeparator = getFileSeparator()
    var buildVariants = ""
    var configurationName = "debugCompileClasspath"
    var taskNameDependencyInsight = ":app:dependencyInsight"

    override fun apply(project: Project) {
        println("gradleVersion=${project.gradle.gradleVersion}")
        println("Implementation-Version=${Utils.getManifestValue("Implementation-Version")}")
        println("KxfBuildTime=${Utils.getManifestValue("KxfBuildTime")}")

        var extensionName = "${GROUP_NAME}_${TASK_NAME}"
        println(
            """
            Configuration parameters in gradle:
            $extensionName{
                //abi
                ABI_TYPES = ["armeabi-v7a", "arm64-v8a"]
            }
        """.trimIndent()
        )
        var extension =
            project.extensions.create("${GROUP_NAME}_${TASK_NAME}", CheckApkSoPluginExt::class.java)

        project.afterEvaluate {
            println("CheckApkSoPluginExt===>>>$extension")
            if (extension.abI_TYPES != null && extension.abI_TYPES.isNotEmpty()) {
                ABI_TYPES = extension.abI_TYPES
            }

            // 1. Find the task merge[BuildVariants]NativeLibs
            var mergeNativeTask: Task? = null
            for (task in project.getTasks()) {
                if (task.name.startsWith("merge") && task.name.endsWith("NativeLibs")) {
                    mergeNativeTask = task
                }
            }
            println("mergeNativeTask => $mergeNativeTask")
            if (null == mergeNativeTask) {
                return@afterEvaluate
            }

            //mergeReleaseNativeLibs
            buildVariants = mergeNativeTask.name.replace("merge", "").replace("NativeLibs", "")
            var firstChar = buildVariants.get(0)
            println("buildVariants => $buildVariants")
            configurationName =
                "${buildVariants.replaceFirst(firstChar, firstChar.lowercaseChar())}CompileClasspath"
            println("configurationName => $configurationName")

            taskNameDependencyInsight = ":${project.name}:dependencyInsight"
            println("taskNameDependencyInsight => $taskNameDependencyInsight")

            // 2. Create detect task.
            var task = project.getTasks().create(TASK_NAME).dependsOn(mergeNativeTask)
            task.group = GROUP_NAME
            task.doLast {
                println("$GROUP_NAME => $TASK_NAME start.")

                // 2.1 Find so file recursively.
                var files = mergeNativeTask.inputs.files.files
                var soMap = HashMap<String, ArrayList<SoFile>>()
                for (abi in ABI_TYPES) {
                    var soFileList = ArrayList<SoFile>()
                    soMap.put(abi, soFileList)
                }
                for (file in files) {
                    toSoMap(soMap, file)
                }
                if (logFlag){
                    println("==========soMap==========")
                    for (key in soMap.keys) {
                        println("$key[${soMap[key]?.size}]==>>>${soMap[key]}")
                    }
                    println("==========soMap==========")
                }


//                for ((key, value) in project.configurations.asMap) {
//                    println("project.configurations==>>${key}:::${value}")
//                }

                var soDiffList = ArrayList<SoFileDiffList>()
                compareMap(soDiffList, soMap, project)
                println("")
                println("==============success==============")
                for (soFileDiffList in soDiffList){
                    println("$soFileDiffList")
                }


                //  gradlew :app:dependencyInsight --configuration debugCompileClasspath --dependency solibtest
//                println("project.projectDir.parentFile==>>${project.projectDir.parentFile}")
//                var gradlewCommandRe = CommandUtils.gradlewCommand(":app:dependencyInsight", "debugCompileClasspath", "solibtest", project.projectDir.parentFile)
//                println("gradlewCommandRe==>>$gradlewCommandRe")
            }

        }
    }

    private fun getDependencyInfo(soFile: SoFile, project: Project) {
        if (isBlank(soFile.dependencyInfo) && soFile.pomName?.startsWith("jetified-") == true) {
            var dependencyName = soFile.pomName?.split("-")?.get(1)
            if (logFlag){
                println("dependencyName==>>$dependencyName")
            }
            var gradlewCommandRe = dependencyName?.let {
                CommandUtils.gradlewCommand(
                    taskNameDependencyInsight, configurationName,
                    it, project.projectDir.parentFile
                )
            }
            gradlewCommandRe = gradlewCommandRe?.let { parseDependencyInfo(it, configurationName) }
            if (!isBlank(gradlewCommandRe)) {
                soFile.dependencyInfo = gradlewCommandRe
            }
        }
    }

    private fun parseDependencyInfo(dependencyInfo: String, configurationName: String): String {
        var list = arrayListOf("")
        try {
            val br = BufferedReader(
                InputStreamReader(
                    dependencyInfo.byteInputStream(),
                    Charsets.UTF_8
                )
            )
            while (true) {
                var line: String? = br.readLine() ?: break
//                println("line==>>$line")
                if (line != null) {
                    if (line.contains("\\---") && !line.contains(configurationName)) {
                        list.add(line.replace("\\---", "").trim())
                    }
                }
            }
            br.close()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return list.last()
    }

    private fun compareMap(
        soDiffList: ArrayList<SoFileDiffList>,
        soMap: java.util.HashMap<String, java.util.ArrayList<SoFile>>,
        project: Project
    ) {
        if (soMap.keys.size >= 2) {
            var keyArray = soMap.keys.toTypedArray()
            for (i in 0 until keyArray.size - 1) {
                var key1 = keyArray[i]
                for (j in i + 1 until keyArray.size) {
                    var key2 = keyArray[j]
                    compareList(soDiffList, soMap, key1, key2, project)
                    compareList(soDiffList, soMap, key2, key1, project)
                }
            }
        }
    }

    private fun compareList(
        soDiffList: ArrayList<SoFileDiffList>,
        soMap: java.util.HashMap<String, java.util.ArrayList<SoFile>>,
        key1: String,
        key2: String,
        project: Project
    ) {
        var list1 = soMap.get(key1)
        var list2 = soMap.get(key2)
        var list1More = ArrayList<SoFile>()
        if (list1 != null && !list1.isEmpty()) {
            if (list2 != null && !list2.isEmpty()) {
                for (item in list1) {
                    if (!list2.contains(item)) {
                        list1More.add(item)
                    }
                }
            } else {
                list1More = list1
            }
        }

        for (item in list1More) {
            getDependencyInfo(item, project)
        }

        var soFileDiffList = SoFileDiffList(key1, key2, list1More)
        soDiffList.add(soFileDiffList)
    }

    private fun toSoMap(soMap: HashMap<String, ArrayList<SoFile>>, file: File?) {
//        println("file => $file")
        file?.let {
            if (file.isDirectory) {
                for (f in file.listFiles()) {
                    toSoMap(soMap, f)
                }
            } else if (file.absolutePath.endsWith(".so")) {
//                toSoMap(soMap, file)
                var so = generateSoInfo(it)
//                println("soFile==>>>$so")
                for (key in soMap.keys) {
                    if (so.soPath?.contains(key + fileSeparator!!) == true) {
                        so.abi = key
                        soMap[key]?.add(so)
                    }
                }
            }
        }
    }

    private fun generateSoInfo(file: File): SoFile {
        var filePath = file.absolutePath

        var so = SoFile(null, filePath, file.name, null, null)

        if (filePath.contains("merged_jni_libs") || filePath.contains("library_jni")) {
            so.pomName = ""
        } else {
            var dirPath = filePath.split(fileSeparator!!)
            so.pomName = dirPath[dirPath.size - 4]
        }
        return so
    }

    @JvmName("getFileSeparator1")
    private fun getFileSeparator(): String? {
        var separator = File.separator
//        if (System.getProperty("os.name").toLowerCase().contains("windows")) {
//            separator = "\\\\"
//        }
        return separator
    }

}



